#include "TextEditMgr.h"
#include "comLogManager.h"
#include "ttcodeStateChart.h"

#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>

using namespace std;


#include "xpm/warning_marker.xpm"
#include "xpm/note_marker.xpm"
#include "xpm/error_marker.xpm"


enum
{
	kMarkerNumberError = wxSTC_MARK_CIRCLE ,
	kMarkerNumberWarning,
	kMarkerNumberNote,
};

enum
{
	kStyleUnderlineError = wxSTC_STYLE_DEFAULT - 16 ,
	kStyleUnderlineWarning,
	kStyleUnderlineNote,
};

// Virtual event handlers, overide them in your derived class
void TextEditMgr::m_auinotebookTextEditOnAuiNotebookPageClose( wxAuiNotebookEvent& event )
{
	int index = event.GetSelection();
	TextEditCtrl *pCtrl = dynamic_cast<TextEditCtrl *>(m_auinotebookTextEdit->GetPage(index));
	if( pCtrl )
	{
		TiXmlElement* elem = pCtrl->getElement();
		if(elem)
		{
			this->tryCloseFile(elem);
		}
		else
		{
			this->tryCloseExternalFile(pCtrl->getPath());
		}
	}

}



void TextEditMgr::tryOpenFile(TiXmlElement* elem)
{
	string name;
	try
	{
		const char *itemName = NULL;
		if( elem )
		{
			itemName = elem->Value();
		}
		name = string(itemName);
	}
	catch(...)
	{
		ttcode_log << "TextEditMgr create error" << endl;
	}
	ttcode_log << "name=" << name << endl;
	if( "compile_file" == name )
	{
	}
	if( "resource_file" == name )
	{
	}
	if( "script_file" == name )
	{
	}

	string id;
	if( "file" == name )
	{
		const char *chId = NULL;
		if( elem )
		{
			chId = elem->Attribute("id");
		}
		if(chId)
		{
			id = string(chId);
		}
		TextEditCtrl *pCtrl = m_EditMgr[id];
		if(pCtrl)
		{
			int index = this->m_auinotebookTextEdit->GetPageIndex(pCtrl);
			this->m_auinotebookTextEdit->SetSelection(index);
		}
		else
		{
			// create a new editor
			m_EditMgr[id] = new TextEditCtrl(this->m_auinotebookTextEdit,elem);
		}
	}
}

void TextEditMgr::tryCloseFile( TiXmlElement* elem )
{

	string id;
	const char *chId = NULL;
	if( elem )
	{
		chId = elem->Attribute("id");
	}
	if(chId)
	{
		id = string(chId);
	}
	ttcode_log << "id=" << id << endl;
	TextEditCtrl *pCtrl = m_EditMgr[id];
	if(pCtrl)
	{
		if( pCtrl->GetModify() )
		{
			pCtrl->SaveFile(pCtrl->m_Path);
		}
		m_EditMgr.erase(id);
	}
}

void TextEditMgr::tryOpenExternalFile(const std::string &filepath)
{
	TextEditCtrl *pCtrl = m_ExtFileMgr[filepath];
	if(pCtrl)
	{
		int index = this->m_auinotebookTextEdit->GetPageIndex(pCtrl);
		this->m_auinotebookTextEdit->SetSelection(index);
	}
	else
	{
		// create a new editor
		m_ExtFileMgr[filepath] = new TextEditCtrl(this->m_auinotebookTextEdit,filepath);
	}
}

void TextEditMgr::tryCloseExternalFile(const std::string &filepath)
{
	TextEditCtrl *pCtrl = m_ExtFileMgr[filepath];
	if(pCtrl)
	{
		if( pCtrl->GetModify() )
		{
			pCtrl->SaveFile(pCtrl->m_Path);
		}
		m_ExtFileMgr.erase(filepath);
	}
}

void TextEditMgr::tryGotoPos(int line,int col,const std::string &info)
{
	int index = m_auinotebookTextEdit->GetSelection();
	wxWindow *pWin = m_auinotebookTextEdit->GetPage(index);
	TextEditCtrl *pCtrl = dynamic_cast<TextEditCtrl *>(pWin);
	if( NULL == pCtrl)
	{
		return;
	}
	ttcode_log << "line=<"<< line << ">" << endl;
	ttcode_log << "col=<"<< col << ">" << endl;

	pCtrl->ScrollToLine( line -6 );
	int pos = pCtrl->PositionFromLine(line -1);
	pCtrl->GotoPos(pos + col -1 );

//	int state = pCtrl->GetLineState(line -1);
//	ttcode_log << "col=<"<< col << ">" << endl;
//	pCtrl->StartStyling(pos + col -1,wxSTC_INDICS_MASK );
//	pCtrl->SetStyling(1,wxSTC_INDIC0_MASK);
}










//const wxBitmap TextEditCtrl::m_ErrorMark(error_marker);
//const wxBitmap TextEditCtrl::m_WarningMark(warning_marker);
//const wxBitmap TextEditCtrl::m_NoteMark(note_marker);

TextEditCtrl::TextEditCtrl( wxAuiNotebook *parent,TiXmlElement* elem)
: wxStyledTextCtrl(parent)
,m_Elem(elem)
,m_Path("")
,m_Lang("")
,m_ErrorMark(error_marker)
,m_WarningMark(warning_marker)
,m_NoteMark(note_marker)
{
	try
	{
		const char *pName = NULL;
		pName = m_Elem->Attribute("name");
		string name;
		if(pName)
		{
			name = string(pName);
		}
		const char *pPath = NULL;
		pPath = m_Elem->Attribute("path");
		if( pPath )
		{
			m_Path = string(pPath);
		}
		const char *pLang = NULL;
		pLang = m_Elem->Attribute("lang");
		if( pLang )
		{
			m_Lang = string(pLang);
		}
		this->loadTextStyle();
		this->loadTextFile();
		this->loadLogInfo();
		parent->AddPage(this,name,true,wxNullBitmap);
	}
	catch(...)
	{
	}
}


TextEditCtrl::TextEditCtrl( wxAuiNotebook *parent,const std::string &path)
: wxStyledTextCtrl(parent)
,m_Elem(NULL)
,m_Path(path)
,m_Lang("")
,m_ErrorMark(error_marker)
,m_WarningMark(warning_marker)
,m_NoteMark(note_marker)
{
	try
	{
		boost::filesystem::path filePath(path);
		string name = filePath.filename();
		this->loadTextStyle();
		this->loadTextFile();
		this->loadLogInfo();
		parent->AddPage(this,name,true,wxNullBitmap);
	}
	catch(...)
	{
	}
}


BEGIN_EVENT_TABLE(TextEditCtrl, wxStyledTextCtrl)
END_EVENT_TABLE()





static string localDefaultCKeyWord[] =
{
	"and","and_eq","asm","auto","bitand","bitor","bool",
	"break","case","catch","char","class","compl","const",
	"const_cast","continue","default","delete","do","double",
	"dynamic_cast","else","enum","explicit","export","extern",
	"false","float","for","friend","goto","if","inline","int",
	"long","mutable","namespace","new","not","not_eq","operator",
	"or","or_eq","private","protected","public","register",
	"reinterpret_cast","return","short","signed","sizeof","static",
	"static_cast","struct","switch","template","this","throw","true",
	"try","typedef","typeid","typename","union","unsigned","using",
	"virtual","void","volatile","wchar_t","while","xor","xor_eq"
};

static const wxColour selfLIGHT_GREEN(0,0x7f,0);
static const wxColour selfLIGHT_RED_GREEN(0x7f,0x7f,0);
static const wxColour selfGREY(0x7f,0x7f,0x7f);


typedef std::map<int,const wxColour*> colorMap;
static colorMap localDefaultCColor = boost::assign::map_list_of \
	( wxSTC_C_DEFAULT,wxBLACK ) \
	( wxSTC_C_COMMENT,&selfLIGHT_GREEN ) \
	( wxSTC_C_COMMENTLINE,&selfLIGHT_GREEN ) \
	( wxSTC_C_COMMENTDOC,&selfLIGHT_GREEN ) \
	( wxSTC_C_NUMBER,wxRED) \
	( wxSTC_C_WORD,wxBLUE) \
	( wxSTC_C_STRING,&selfGREY) \
	( wxSTC_C_CHARACTER,wxRED) \
	( wxSTC_C_UUID,wxRED) \
	( wxSTC_C_PREPROCESSOR,&selfLIGHT_RED_GREEN) \
	( wxSTC_C_OPERATOR,wxBLUE) \
	( wxSTC_C_IDENTIFIER,wxBLACK) \
	( wxSTC_C_STRINGEOL,wxRED) \
	( wxSTC_C_VERBATIM,wxRED) \
	( wxSTC_C_REGEX,wxLIGHT_GREY) \
	( wxSTC_C_COMMENTLINEDOC,wxRED) \
	( wxSTC_C_WORD2,wxBLUE) \
	( wxSTC_C_COMMENTDOCKEYWORD,&selfLIGHT_GREEN) \
	( wxSTC_C_COMMENTDOCKEYWORDERROR,&selfLIGHT_GREEN) \
	( wxSTC_C_GLOBALCLASS ,wxCYAN );

void TextEditCtrl::loadTextStyle(void)
{
	this->ClearDocumentStyle();

	wxFont *font = new wxFont(12,wxDEFAULT,wxNORMAL,wxNORMAL);
	
	this->StyleSetFont(wxSTC_STYLE_DEFAULT,*font);
	this->SetEOLMode(wxSTC_EOL_LF);
	this->SetTabWidth(4);
	

	// display line number
	this->SetMarginType(0,wxSTC_MARGIN_NUMBER);
//	this->SetMarginMask(0,0x1);
	int lineWidth = 5* this->TextWidth(wxSTC_STYLE_LINENUMBER, wxT("9"));
	this->SetMarginWidth(0,lineWidth);

	// 
	this->SetMarginType(1,wxSTC_MARGIN_SYMBOL);
	this->SetMarginMask(1,~wxSTC_MASK_FOLDERS );
	this->SetMarginWidth(1,16);

// marker
	this->MarkerDefineBitmap( kMarkerNumberError ,m_ErrorMark );
	this->MarkerDefineBitmap( kMarkerNumberWarning ,m_WarningMark );
	this->MarkerDefineBitmap( kMarkerNumberNote ,m_NoteMark );

// marker
// folder
 	this->SetMarginType(2, wxSTC_MARGIN_SYMBOL);
	this->SetMarginMask(2, wxSTC_MASK_FOLDERS);
	this->StyleSetBackground (2, *wxWHITE);
	this->SetMarginWidth(2,16);
    this->SetMarginSensitive (2, true);
    this->MarkerDefine(wxSTC_MARKNUM_FOLDER,        wxSTC_MARK_DOTDOTDOT, _T("BLACK"), _T("BLACK"));
    this->MarkerDefine(wxSTC_MARKNUM_FOLDEROPEN,    wxSTC_MARK_ARROWDOWN, _T("BLACK"), _T("BLACK"));
    this->MarkerDefine(wxSTC_MARKNUM_FOLDERSUB,     wxSTC_MARK_EMPTY,     _T("BLACK"), _T("BLACK"));
    this->MarkerDefine(wxSTC_MARKNUM_FOLDEREND,     wxSTC_MARK_DOTDOTDOT, _T("BLACK"), _T("WHITE"));
    this->MarkerDefine(wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_ARROWDOWN, _T("BLACK"), _T("WHITE"));
    this->MarkerDefine(wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_EMPTY,     _T("BLACK"), _T("BLACK"));
    this->MarkerDefine(wxSTC_MARKNUM_FOLDERTAIL,    wxSTC_MARK_EMPTY,     _T("BLACK"), _T("BLACK"));
	this->SetProperty("fold","1");
	this->SetProperty("fold.comment","1");
	this->SetProperty("fold.preprocessor","1");
	this->SetProperty("fold.compact","1");
	this->SetProperty("styling.within.preprocessor","1");


    this->SetFoldFlags (wxSTC_FOLDFLAG_LINEBEFORE_CONTRACTED | wxSTC_FOLDFLAG_LINEAFTER_CONTRACTED);

// style
//	this->StyleSetUnderline(kStyleUnderlineError,true);
//	this->StyleSetUnderline(kStyleUnderlineWarning,true);
//	this->StyleSetUnderline(kStyleUnderlineNote,true);
//	this->IndicatorSetStyle(0,kStyleUnderlineError );
//	this->IndicatorSetForeground(0, *wxRED );


	string type;
	if( !m_Lang.empty() )
	{
		string strLang = boost::to_lower_copy(m_Lang);
		type = globalFileType[strLang];
	}
	else if( !m_Path.empty() )
	{
		boost::filesystem::path fsPath(m_Path);
		string strLang = boost::filesystem::extension(fsPath);
		boost::to_lower(strLang);
		type = globalFileType[strLang];
	}
	string allWord;
	if( "head" == type || "cstd" == type || "cpp" == type || "cl" == type )
	{
		BOOST_FOREACH( string word,localDefaultCKeyWord )
		{
			allWord += word + " ";
		}
		this->SetLexer(wxSTC_LEX_CPP);
		this->SetKeyWords(0,allWord);

		BOOST_FOREACH( colorMap::value_type &i,localDefaultCColor )
		{
			this->StyleSetForeground(i.first,*(i.second));
		}
	}
}

void TextEditCtrl::loadTextFile(void)
{
	boost::filesystem::path fsPath(m_Path);
	if( boost::filesystem::exists(fsPath) )
	{
		ifstream filestr (fsPath.string().c_str());
		filebuf *pBuff = filestr.rdbuf();
		long size = pBuff->pubseekoff (0,ios::end,ios::in);
		pBuff->pubseekpos (0,ios::in);
		char *buffer = new char[size + 1];
		memset(buffer,0x0,size + 1);
		pBuff->sgetn(buffer,size);
		this->AddText(buffer);
		delete [] buffer;
	}
	else
	{
	}
}


void TextEditCtrl::loadLogInfo(void)
{
	if( NULL == m_Elem )
	{
		ttcode_log << "error" << endl;
		return;
	}
	TiXmlElement* log = m_Elem->FirstChildElement("logInfo");
	if( NULL == log )
	{
		ttcode_log << "error" << endl;
		return;
	}
	TiXmlElement* build = log->FirstChildElement("build");
	if( NULL == build )
	{
		ttcode_log << "error" << endl;
		return;
	}
	const char *pLogPath = build->GetText();
	if( NULL == pLogPath)
	{
		ttcode_log << "error" << endl;
		return;
	}
	boost::filesystem::path pathOut( pLogPath );
	if( false == boost::filesystem::exists(pathOut) )
	{
		ttcode_log << "error" << endl;
		return;
	}
	try
	{
		ifstream ifs ( pLogPath , ifstream::in );
		while( ifs.good() && !ifs.eof() )
		{
			char linebuf[1024];
			memset(linebuf,0x0,sizeof(linebuf));
			ifs.getline(linebuf,sizeof(linebuf));
			string buf(linebuf);
//			ttcode_log << "buf=<"<< buf << ">" << endl;
			boost::smatch what;
			if( boost::regex_search(buf,what, boost::regex(":\\d+:\\d+:")) )
			{
				char lineTips[1024];
				memset(lineTips,0x0,sizeof(lineTips));
				ifs.getline(lineTips,sizeof(lineTips));
				ifs.getline(lineTips,sizeof(lineTips));
				this->reportInfo(buf,string(lineTips) );
			}
		}
		ifs.close();
	}
	catch(...)
	{}
	ttcode_log << "error" << endl;
}
void TextEditCtrl::reportInfo(const std::string &info,const std::string &tip)
{
	ttcode_log << "info=<"<< info << ">" << endl;
	ttcode_log << "tip=<"<< tip << ">" << endl;
	ttcode_log << "m_Path=<"<< m_Path << ">" << endl;
	int line,col;
	line = col = 0;
	boost::smatch what;
	if( false == boost::regex_search(info,what, boost::regex(m_Path)) )
	{
		return ;
	}
	if( boost::regex_search(info,what, boost::regex(":\\d+:\\d+:")) )
	{
//		ttcode_log << "lineNum=<"<< lineNum << ">" << endl;
//		ttcode_log << "textLine=<"<< textLine << ">" << endl;
		string delimit(what[0]);
//		ttcode_log << "delimit=<"<< delimit << ">" << endl;
		size_t pos = info.find(delimit);
		if ( string::npos != pos )
		{
			string filepath = info.substr(0,pos);
			ttcode_log << "filepath=<"<< filepath << ">" << endl;
			if( boost::regex_search(delimit,what, boost::regex(":(\\d+):(\\d+):")) )
			{
				string lineStr(what[1]);
				string colStr(what[2]);
				ttcode_log << "lineStr=<" << lineStr << ">" << endl;
				ttcode_log << "colStr=<" << colStr << ">" << endl;
				try
				{
					line = boost::lexical_cast<int>(lineStr);
					col = boost::lexical_cast<int>(colStr);
					int pos = this->PositionFromLine(line -1);
					ttcode_log << "pos=<" << pos << ">" << endl;

					string::size_type start = tip.find_first_of("^");
					string::size_type end = tip.find_last_of("^");
					ttcode_log << "start=<" << start << ">" << endl;
					ttcode_log << "end=<" << end << ">" << endl;
					if( string::npos != start && string::npos != end )
					{
						this->StartStyling( pos + start,wxSTC_INDICS_MASK );
						this->SetStyling( end - start +1,wxSTC_INDIC1_MASK );
					}
					start = tip.find_first_of("~");
					end = tip.find_last_of("~");
					ttcode_log << "start=<" << start << ">" << endl;
					ttcode_log << "end=<" << end << ">" << endl;
					if( string::npos != start && string::npos != end )
					{
						this->StartStyling( pos + start,wxSTC_INDICS_MASK );
						this->SetStyling( end - start +1,wxSTC_INDIC0_MASK );
					}
				}
				catch(boost::bad_lexical_cast & e)
				{
				}
			}
		}
		ttcode_log << "line -1=<"<< line -1 << ">" << endl;
		if( boost::regex_search(info,what, boost::regex(":\\d+:\\d+: error:") ))
		{
			ttcode_log << "error" << endl;
			this->MarkerAdd(line -1,kMarkerNumberError );
		}
		if( boost::regex_search(info,what, boost::regex(":\\d+:\\d+: fatal error:") ))
		{
			ttcode_log << "fatal error" << endl;
			this->MarkerAdd(line -1,kMarkerNumberError );
		}
		if( boost::regex_search(info,what, boost::regex(":\\d+:\\d+: warning:") ))
		{
			ttcode_log << "warning" << endl;
			this->MarkerAdd(line -1,kMarkerNumberWarning );
		}
		if( boost::regex_search(info,what, boost::regex(":\\d+:\\d+: note:") ))
		{
			ttcode_log << "note" << endl;
			this->MarkerAdd(line -1,kMarkerNumberNote );
		}
	}
}
